#    Rubatom
#    Copyright (c) 2014 Sebastian Dufner
#
#    This file is part of Rubatom.
#
#    Rubatom is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    Rubatom is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.

require 'delegate'

require 'rubatom/template/block'
require 'rubatom/template/link'

class NbtIO < SimpleDelegator
  Tag = Struct.new(:id, :name, :payload)

  def read_nbt
    tags = []
    id, name, payload = nil

    begin
      id = read_tag_id
      puts id
      name = read_tag_string
      payload = read_tag id
    end until id == 0

    Tag.new(id, name, payload)
  rescue Exception => error
    puts tags.to_s
    fail error
  end

  def read_tag(tag)
    case tag
    when 0, :end          then Tag.new(0, "", nil)
    when 1, :byte         then read_tag_byte
    when 2, :short        then read_tag_short
    when 3, :int          then read_tag_int
    when 4, :long         then read_tag_long
    when 5, :float        then read_tag_float
    when 6, :double       then read_tag_double
    when 7, :byte_array   then read_tag_byte_array
    when 8, :string       then read_tag_string
    when 9, :list         then read_tag_list
    when 10, :compound    then read_tag_compound
    when 11, :int_array   then read_tag_int_array
    else fail "#{tag}"
    end
  end

  def read_tag_id
    read(1).unpack('c')[0]
  end

  def read_tag_byte
    read_tag_id
  end

  def read_tag_short
    read(2).unpack('s>')[0]
  end

  def read_tag_int
    read(4).unpack('i>')[0]
  end

  def read_tag_long
    read(8).unpack('q>')[0]
  end

  def read_tag_float
    read(4).unpack('f>')[0]
  end

  def read_tag_double
    read(8).unpack('d>')[0]
  end

  def read_tag_byte_array
    read_many(*([:int8] * read_tag_int))
  end

  def read_tag_string
    read(read_tag_short)
  end

  def read_tag_list
    id, length = *read_many(:int8, :int32)
    length.times.map {read_tag(id)}
  end

  def read_tag_compound
    read_nbt
  end

  def read_tag_int_array
    read_many(*([:int32] * read_tag_int))
  end

end

module Rubatom
  class Template
    module From
      module NBT
        Block_convert = lambda do |id, data|
          case id
          when 0    then nil
          when 2    then 73    # stone        -> rock
          when 3    then 82    # dirt         -> grass/dirt
          when 4    then 73    # cobblestone  -> rock
          when 17
            case data
            when 0, 2   then 84   # wood      -> wood
            when 1, 5   then 276  # dark wood -> ice wood
            when 3, 4   then 287  # red wood  -> redwood
            end
          else warn "unknown block ID #{id}, ignoring it"
          end
        end

        def self.schematic(input)
          nbt = NbtIO.new(input).read_nbt

          width = nbt.payload.find {|tag| tag.name == 'Width'}.payload
          height = nbt.payload.find {|tag| tag.name == 'Height'}.payload
          length = nbt.payload.find {|tag| tag.name == 'Length'}.payload
          blocks = nbt.payload.find {|tag| tag.name == 'Blocks'}.payload
              .zip(nbt.payload.find {|tag| tag.name == 'Data'}.payload)

          n_blocks = width * height * length
          fail "computed number of blocks (#{n_blocks}) != real number of blocks (#{blocks.size})" if
            n_blocks != blocks.size

          i = 0
          length.times do |z|
            height.times do |y|
              width.times do |x|
                blocks[i] = yield *blocks[i]

                i += 1
              end
              i += 1
            end
            i += 1
          end

          Template.new(
                Header.new(1, 6, 8, 6, 9, 9, 8),    # Don't ask, no idea.
                Blocks.new(blocks.compact!),
                Links.new([]))
        end

      end
    end
  end
end
